﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections;
using System.Windows.Data;
using System.ComponentModel;

namespace Jib.Controls.DataGrid
{
    public partial class JibGrid : System.Windows.Controls.DataGrid
    {  
        #region FilteredItemsSource DependencyProperty
        public static readonly DependencyProperty FilteredItemsSourceProperty =
                                                                DependencyProperty.Register("FilteredItemsSource", typeof(IEnumerable), typeof(JibGrid),
                                                                new PropertyMetadata(null, new PropertyChangedCallback(OnFilteredItemsSourceChanged)));

        public IEnumerable FilteredItemsSource
        {
            get { return (IEnumerable)GetValue(FilteredItemsSourceProperty); }
            set { SetValue(FilteredItemsSourceProperty, value); }
        }

        public static void OnFilteredItemsSourceChanged(object sender, DependencyPropertyChangedEventArgs args)
        {
            if (!DesignerProperties.IsInDesignTool)
            {
                JibGrid fil = sender as JibGrid;
                PagedCollectionView view = new PagedCollectionView((IEnumerable)args.NewValue);
                Type srcT = args.NewValue.GetType().GetInterfaces().First(i => i.Name.StartsWith("IEnumerable"));
                fil.FilterType = srcT.GetGenericArguments().First();
                fil.OrginalSource = (IEnumerable)args.NewValue;
                if (fil.AutoGenerateColumns)
                {
                    fil.FilterHeaders.Clear();
                    fil.ColumnOptionControls.Clear();
                }
                fil.ItemsSource = view;
                fil.ResetColumnHeaders();
            }
        }

        #endregion

        public List<ColumnFilterControl> FilterHeaders { get; set; }

        public List<ColumnOptionControl> ColumnOptionControls { get; set; }

        private List<PropertyGroupDescription> ColumnGroups { get; set; }

        private bool _CanGroup = true;
        [Bindable(false)]
        [Category("Appearance")]
        [DefaultValue("True")]
        public bool CanGroup
        {
            set
            {
                _CanGroup = value;
                foreach (var option in ColumnOptionControls)
                    option.CanGroup = value;

            }
        }

        private bool _CanPin = true;
        [Bindable(false)]
        [Category("Appearance")]
        [DefaultValue("True")]
        public bool CanPin
        {
            set
            {
                _CanPin = value;
                foreach (var option in ColumnOptionControls)
                    option.CanPin = value;
            }
        }

        private bool ColumnAutoGenerated { get; set; }

        public Type FilterType { get; set; }

        public IEnumerable OrginalSource { get; set; }
      
        public JibGrid()
        {
            FilterHeaders = new List<ColumnFilterControl>();
            ColumnOptionControls = new List<ColumnOptionControl>();
            ColumnGroups = new List<PropertyGroupDescription>();
            InitializeComponent();
  
        }

        //Since the ColumnFilterHeader control is defined in the headerStyle for the grid, we can only get a reference to it once it loads.  We
        //then we have to find the column that the ColumnFilterHeader is associated with.  We do this by comparing the Header 
        //and HeaderContent for each column.
        private void ColumnFilterHeader_Loaded(object sender, RoutedEventArgs e)
        {
            var header = sender as ColumnFilterControl;
            FilterHeaders.Add(header);
            if (header.HeaderContent != null)
            {
                var column = this.Columns.Where(c => c.Header != null && c.Header.ToString() == header.HeaderContent.ToString()).FirstOrDefault();
                header.Column = column;
                header.Grid = this;
                header.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(header_PropertyChanged);
                if (this.FilterType != null)
                    header.ResetFilterValues(CreateFilterColumnInfo(column));
            }

        }

        //Since the ColumnOptionControl control is defined in the headerStyle for the grid, we can only get a reference to it once it loads.
        //We then have to find the column that the ColumnOptionControl associated with.  We do this by comparing the
        //Column Hedaer, and the control Tags property
        private void ColumnOptionControl_Loaded(object sender, RoutedEventArgs e)
        {
            var optionCtrl = sender as ColumnOptionControl;
            ColumnOptionControls.Add(optionCtrl);
            if (optionCtrl.Tag != null)
            {
                var column = this.Columns.Where(c => c.Header != null && c.Header.ToString() == optionCtrl.Tag.ToString()).FirstOrDefault();
                optionCtrl.Column = column;
                optionCtrl.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(optionCtrl_PropertyChanged);
                if (this.FilterType != null)
                    optionCtrl.ResetOptionValues(CreateFilterColumnInfo(column));
            }
        }

        //If the user selects a column option.  We need to perform the operation
        private void optionCtrl_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            var optionCtrl = sender as ColumnOptionControl;
            if (e.PropertyName == "SelectedColumnOptionItem")
            {
                switch (optionCtrl.SelectedColumnOptionItem.ColumnOption)
                {
                    case Enums.ColumnOption.AddGrouping:
                        optionCtrl.IsGrouped = true;
                        PagedCollectionView v = this.ItemsSource as PagedCollectionView;
                        PropertyGroupDescription newGroup = new PropertyGroupDescription(optionCtrl.FilterColumnInfo.PropertyPath, optionCtrl.FilterColumnInfo.Converter);
                        ColumnGroups.Add(newGroup);
                        v.GroupDescriptions.Add(newGroup);
                        break;
                    case Enums.ColumnOption.RemoveGrouping:
                        optionCtrl.IsGrouped = false;
                        PagedCollectionView view = this.ItemsSource as PagedCollectionView;
                        PropertyGroupDescription group = ColumnGroups.Where(c => c.PropertyName == optionCtrl.FilterColumnInfo.PropertyPath).FirstOrDefault();
                        if (group != null)
                        {
                            ColumnGroups.Remove(group);
                            view.GroupDescriptions.Remove(group);
                        }
                        break;
                    case Enums.ColumnOption.PinColumn:
                        optionCtrl.IsPinned = true;
                        int frozenCount = this.FrozenColumnCount;
                        if (optionCtrl.Column.DisplayIndex >= this.FrozenColumnCount)
                            frozenCount++;
                        optionCtrl.Column.DisplayIndex = 0;
                        this.FrozenColumnCount = frozenCount;
                        break;
                    case Enums.ColumnOption.UnpinColumn:
                        optionCtrl.IsPinned = false;
                        optionCtrl.Column.DisplayIndex = this.FrozenColumnCount - 1;
                        this.FrozenColumnCount = this.FrozenColumnCount - 1;
                        break;
                }
            }
        }

        //Simple helper method which creates the FilterColumnInfo object for a column.  If a binding
        //is defined for the column we use that.  If that fails we fallback to using the SortMemberPath
        private FilterColumnInfo CreateFilterColumnInfo(DataGridColumn column)
        {

            FilterColumnInfo col = new FilterColumnInfo();
            DataGridBoundColumn boundColumn = column as DataGridBoundColumn;
            if (column != null)
            {
                if (boundColumn != null)
                {
                    if (!string.IsNullOrWhiteSpace(boundColumn.Binding.Path.Path))
                    {
                        col.PropertyPath = boundColumn.Binding.Path.Path;
                        col.PropertyType = FilterType.GetProperty(boundColumn.Binding.Path.Path).PropertyType;
                        col.Converter = boundColumn.Binding.Converter;
                        col.ConverterCultureInfo = boundColumn.Binding.ConverterCulture;
                        col.ConverterParameter = boundColumn.Binding.ConverterParameter;
                    }
                }
                else if (column.SortMemberPath.Length > 0)
                {
                    col.PropertyPath = column.SortMemberPath;
                    col.PropertyType = FilterType.GetProperty(column.SortMemberPath).PropertyType;
                }
            }
            return col;
        }

        //Each time any ColumnFilterHeader values change.  We need to generate the new predicate for the PagedCollectionView
        private void header_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            PagedCollectionView view = this.ItemsSource as PagedCollectionView;
            Predicate<object> predicate = null;
            foreach (var filter in FilterHeaders)
                if (filter.HasPredicate)
                    if (predicate == null)
                        predicate = filter.GeneratePredicate();
                    else
                        predicate = predicate.And(filter.GeneratePredicate());
            view.Filter = predicate;
        }

        /// <summary>
        /// Reset the Filter values for every column
        /// </summary>
        public void ResetColumnHeaders()
        {
            foreach (var header in FilterHeaders)
                header.ResetFilterValues(CreateFilterColumnInfo(header.Column));
            foreach (var option in ColumnOptionControls)
                option.ResetOptionValues(CreateFilterColumnInfo(option.Column));
        }

        /// <summary>
        /// Set visibility of the filter operations for a specific column
        /// </summary>
        /// <param name="column">The column</param>
        /// <param name="showFilterHeader">Show Filter Header</param>
        /// <param name="showColumnOptions">Show Column Options</param>
        public void ConfigureFilterColumn(DataGridColumn column, bool showFilterHeader, bool showColumnOptions)
        {
            var filter = FilterHeaders.Where(f => f.Column == column).FirstOrDefault();
            if (filter != null)
            {
                if (showFilterHeader)
                    filter.Visibility = System.Windows.Visibility.Visible;
                else
                    filter.Visibility = System.Windows.Visibility.Collapsed;
            }
            var option = ColumnOptionControls.Where(o => o.Column == column).FirstOrDefault();
            if (option != null)
            {
                if (showColumnOptions)
                    option.Visibility = System.Windows.Visibility.Visible;
                else
                    option.Visibility = System.Windows.Visibility.Collapsed;
            }
        }
    }
}
